package id.mankel.commons.mybatis;

import id.mankel.commons.constants.Asciis;
import lombok.Builder;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.annotation.ProviderMethodResolver;

import java.util.StringJoiner;

public class BaseSqlProvider implements ProviderMethodResolver {

    public static final String TABLE_NAME       = "tableName";
    public static final String COLUMN_NAME      = "columnName";
    public static final String KEY_PROPERTY     = "keyProperty";

    public static final String FOR_EACH_COLUMNS         = "columns";
    public static final String FOR_EACH_COLUMNS_ITEM    = "c";
    public static final String FOR_EACH_LIST            = "list";
    public static final String FOR_EACH_LIST_ITEM       = "item";
    public static final String FOR_EACH_CONTENT         = "#{item}";
    public static final String FOR_EACH_OPEN_CASE       = "case ${key.column} ";
    public static final String FOR_EACH_CLOSE_CASE      = " else null end";

    private static final String OPEN_SCRIPT         = "<script>";
    private static final String CLOSE_SCRIPT        = "</script>";
    private static final String TEMPLATE_FOREACH    = "<foreach collection='%s' item='%s' separator='%s'>%s</foreach>";
    private static final String TEMPLATE_INSERT     = OPEN_SCRIPT + "insert into ${tableName} (%s) values %s" + CLOSE_SCRIPT;
    private static final String TEMPLATE_UPDATE     = OPEN_SCRIPT + "update ${tableName} set %s where ${key.column} in (%s)" + CLOSE_SCRIPT;
    private static final String TEMPLATE_DELETE     = OPEN_SCRIPT + "delete from ${tableName} where ${columnName} in (%s)" + CLOSE_SCRIPT;

    public static String insert() {

        String forEachColumn = ForEachBuilder.builder()
                .collection(FOR_EACH_COLUMNS)
                .item(FOR_EACH_COLUMNS_ITEM)
                .content("${c.column}")
                .build().toString();
        String forEachValueInner = ForEachBuilder.builder()
                .collection(FOR_EACH_COLUMNS)
                .item(FOR_EACH_COLUMNS_ITEM)
                .content("#{item.${c.property}}")
                .open(Asciis.OPEN_PAREN.s)
                .close(Asciis.CLOSE_PAREN.s)
                .build().toString();
        String forEachValue = ForEachBuilder.builder()
                .content(forEachValueInner)
                .build().toString();

        return String.format(TEMPLATE_INSERT, forEachColumn, forEachValue);
    }

    public static String update() {

        String forEachSetInner = ForEachBuilder.builder()
                .separator(Asciis.SPACE.s)
                .open(FOR_EACH_OPEN_CASE)
                .close(FOR_EACH_CLOSE_CASE)
                .content("when #{item.${key.property}} then #{item.${c.property}}")
                .build().toString();
        String forEachSet = ForEachBuilder.builder()
                .collection(FOR_EACH_COLUMNS)
                .item(FOR_EACH_COLUMNS_ITEM)
                .content("${c.column} = " + forEachSetInner)
                .build().toString();
        String forEachWhere = ForEachBuilder.builder()
                .content("#{item.${key.property}}")
                .build().toString();

        return String.format(TEMPLATE_UPDATE, forEachSet, forEachWhere);
    }


    public static String delete() {
        return String.format(TEMPLATE_DELETE, ForEachBuilder.builder().build().toString());
    }


    // ==================================================

    @Builder
    private static class ForEachBuilder {
        @Builder.Default
        private String collection = FOR_EACH_LIST;
        @Builder.Default
        private String item = FOR_EACH_LIST_ITEM;
        @Builder.Default
        private String separator = Asciis.COMMA.s;
        @Builder.Default
        private String content = FOR_EACH_CONTENT;
        private String open;
        private String close;

        @Override
        public String toString() {
            String s = String.format(TEMPLATE_FOREACH, this.collection, this.item, this.separator, this.content);
            if (StringUtils.isAnyBlank(this.open, this.close)) {
                return s;
            }
            return new StringJoiner(StringUtils.EMPTY, this.open, this.close).add(s).toString();
        }
    }

}
